// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// 引入iPanel.Model命名空间，包含仪表盘的通用模型类
// 引入iPanel.Model.Otlp命名空间，包含OpenTelemetry协议相关的模型类
using iPanel.Model.Otlp;
// 引入iPanel.Otlp.Model命名空间，提供OTLP模型相关的类和接口
// 引入iPanel.Otlp.Storage命名空间，涉及OTLP数据存储相关的功能
// 引入iPanel.Utils命名空间，包含仪表盘工具类，提供实用功能
// 引入Microsoft.AspNetCore.Components命名空间，提供ASP.NET Core组件开发所需的基础类和接口
using Microsoft.AspNetCore.Components;
// 引入Microsoft.FluentUI.AspNetCore.Components命名空间，提供Fluent UI风格的组件
// 引入Microsoft.JSInterop命名空间，用于实现C#代码与JavaScript代码之间的交互
using Microsoft.JSInterop;

namespace iPanel.Components.Controls;

/// <summary>
/// 该组件用于显示OtlpSpan的详细信息，包括属性、事件、链接等，
/// 并支持对这些信息进行过滤和查看详情操作。
/// </summary>
public partial class SpanDetails : IDisposable
{
    /// <summary>
    /// 该参数为必需参数，用于提供Span的详细信息视图模型。
    /// </summary>
    [Parameter, EditorRequired]
    public required SpanDetailsViewModel ViewModel { get; set; }

    /// <summary>
    /// 通过依赖注入获取对话框服务，用于显示对话框。
    /// </summary>
    [Inject]
    public required IDialogService DialogService { get; init; }

    /// <summary>
    /// 通过依赖注入获取导航管理器，用于页面导航。
    /// </summary>
    [Inject]
    public required NavigationManager NavigationManager { get; init; }

    /// <summary>
    /// 通过依赖注入获取遥测数据仓库，用于获取和管理遥测数据。
    /// </summary>
    [Inject]
    public required TelemetryRepository TelemetryRepository { get; init; }

    /// <summary>
    /// 通过依赖注入获取JavaScript运行时，用于执行JavaScript代码。
    /// </summary>
    [Inject]
    public required IJSRuntime JS { get; init; }

    /// <summary>
    /// 获取经过过滤的属性项集合，基于ViewModel中的属性和过滤条件。
    /// </summary>
    private IQueryable<TelemetryPropertyViewModel> FilteredItems =>
        ViewModel.Properties.Where(ApplyFilter).AsQueryable();

    /// <summary>
    /// 获取经过过滤的上下文属性项集合，基于上下文属性和过滤条件。
    /// </summary>
    private IQueryable<TelemetryPropertyViewModel> FilteredContextItems =>
        _contextAttributes.Where(ApplyFilter).AsQueryable();

    /// <summary>
    /// 获取经过过滤的资源属性项集合，基于ViewModel中的资源属性和过滤条件。
    /// 此部分代码未完成，后续可能需要补充获取资源属性的逻辑。
    /// </summary>
    private IQueryable<TelemetryPropertyViewModel> FilteredResourceItems =>
        ViewModel.Span.Source.AllProperties().Select(p => new TelemetryPropertyViewModel { Name = p.DisplayName, Key = p.Key, Value = p.Value })
            .Where(ApplyFilter).AsQueryable();

    /// <summary>
    /// 获取经过过滤的跨度事件集合，仅包含名称包含过滤关键字的事件，并按时间排序。
    /// </summary>
    private IQueryable<OtlpSpanEvent> FilteredSpanEvents =>
        ViewModel.Span.Events.Where(e => e.Name.Contains(_filter, StringComparison.CurrentCultureIgnoreCase)).OrderBy(e => e.Time).AsQueryable();

    /// <summary>
    /// 获取经过过滤的跨度链接视图模型集合，仅包含跨度ID包含过滤关键字的链接。
    /// </summary>
    private IQueryable<SpanLinkViewModel> FilteredSpanLinks =>
        ViewModel.Links.Where(e => e.SpanId.Contains(_filter, StringComparison.CurrentCultureIgnoreCase)).AsQueryable();

    /// <summary>
    /// 获取经过过滤的跨度反向链接视图模型集合，仅包含跨度ID包含过滤关键字的反向链接。
    /// </summary>
    private IQueryable<SpanLinkViewModel> FilteredSpanBacklinks =>
        ViewModel.Backlinks.Where(e => e.SpanId.Contains(_filter, StringComparison.CurrentCultureIgnoreCase)).AsQueryable();

    /// <summary>
    /// 表示跨度事件部分是否展开的标志，初始值为false。
    /// </summary>
    private bool _isSpanEventsExpanded;
    /// <summary>
    /// 表示跨度链接部分是否展开的标志，初始值为false。
    /// </summary>
    private bool _isSpanLinksExpanded;
    /// <summary>
    /// 表示跨度反向链接部分是否展开的标志，初始值为false。
    /// </summary>
    private bool _isSpanBacklinksExpanded;

    /// <summary>
    /// 当前的过滤关键字，初始为空字符串。
    /// </summary>
    private string _filter = "";
    /// <summary>
    /// 上下文属性视图模型列表，初始值为null，在后续逻辑中会被赋值。
    /// </summary>
    private List<TelemetryPropertyViewModel> _contextAttributes = null!;
    /// <summary>
    /// 表示数据是否发生变化的标志，用于在数据变化时执行特定操作。
    /// </summary>
    private bool _dataChanged;
    /// <summary>
    /// 用于存储当前的视图模型，方便后续对比和更新操作。
    /// </summary>
    private SpanDetailsViewModel? _viewModel;

    /// <summary>
    /// 列大小调整的标签，初始化为默认值。
    /// </summary>
    private ColumnResizeLabels _resizeLabels = ColumnResizeLabels.Default;
    /// <summary>
    /// 列排序的标签，初始化为默认值。
    /// </summary>
    private ColumnSortLabels _sortLabels = ColumnSortLabels.Default;

    /// <summary>
    /// 用于取消异步操作的令牌源，在组件销毁时会被取消和释放。
    /// </summary>
    private readonly CancellationTokenSource _cts = new();

    /// <summary>
    /// 应用过滤条件的方法，检查属性视图模型的名称或值是否包含过滤关键字。
    /// </summary>
    /// <param name="vm">要检查的属性视图模型。</param>
    /// <returns>如果包含过滤关键字返回true，否则返回false。</returns>
    private bool ApplyFilter(TelemetryPropertyViewModel vm)
    {
        return vm.Name.Contains(_filter, StringComparison.CurrentCultureIgnoreCase) ||
            vm.Value?.Contains(_filter, StringComparison.CurrentCultureIgnoreCase) == true;
    }

    /// <summary>
    /// 在组件初始化时调用，创建网格标签用于列大小调整和排序。
    /// </summary>
    protected override void OnInitialized()
    {
        // 调用工具类方法创建网格标签
        (_resizeLabels, _sortLabels) = DashboardUIHelpers.CreateGridLabels(Loc);
    }

    /// <summary>
    /// 在组件参数设置时调用，检查视图模型是否更改，更新上下文属性和展开状态。
    /// </summary>
    protected override void OnParametersSet()
    {
        // 检查传入的ViewModel是否与之前的_viewModel不同
        if (!ReferenceEquals(ViewModel, _viewModel))
        {
            // 仅当查看的项发生变化时设置数据更改标志
            if (!string.Equals(ViewModel.Span.SpanId, _viewModel?.Span.SpanId, StringComparisons.OtlpSpanId))
            {
                _dataChanged = true;
            }

            // 更新当前的视图模型
            _viewModel = ViewModel;

            // 初始化上下文属性列表
            _contextAttributes =
            [
                // 添加源信息到上下文属性
                new TelemetryPropertyViewModel { Name = "Source", Key = KnownSourceFields.NameField, Value = _viewModel.Span.Scope.ScopeName }
            ];
            // 如果跨度作用域有版本信息，添加版本到上下文属性
            if (!string.IsNullOrEmpty(_viewModel.Span.Scope.Version))
            {
                _contextAttributes.Add(new TelemetryPropertyViewModel { Name = "Version", Key = KnownSourceFields.VersionField, Value = _viewModel.Span.Scope.Version });
            }
            // 如果跨度有父跨度ID，添加父跨度ID到上下文属性
            if (!string.IsNullOrEmpty(_viewModel.Span.ParentSpanId))
            {
                _contextAttributes.Add(new TelemetryPropertyViewModel { Name = "ParentId", Key = KnownTraceFields.ParentIdField, Value = _viewModel.Span.ParentSpanId });
            }
            // 如果跨度有跟踪ID，添加跟踪ID到上下文属性
            if (!string.IsNullOrEmpty(_viewModel.Span.TraceId))
            {
                _contextAttributes.Add(new TelemetryPropertyViewModel { Name = "TraceId", Key = KnownTraceFields.TraceIdField, Value = _viewModel.Span.TraceId });
            }

            // 当详情部分没有数据时折叠它们
            _isSpanEventsExpanded = _viewModel.Span.Events.Any();
            _isSpanLinksExpanded = _viewModel.Span.Links.Any();
            _isSpanBacklinksExpanded = _viewModel.Span.BackLinks.Any();
        }
    }

    /// <summary>
    /// 在组件渲染完成后异步调用，如果数据发生变化则滚动到顶部。
    /// </summary>
    /// <param name="firstRender">是否为首次渲染。</param>
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (_dataChanged)
        {
            if (!firstRender)
            {
                // 调用JavaScript方法滚动到指定容器的顶部
                await JS.InvokeVoidAsync("scrollToTop", ".property-grid-container");
            }

            // 重置数据变化标志
            _dataChanged = false;
        }
    }

    /// <summary>
    /// 处理查看详情的异步操作，等待跨度数据可用并导航到详情页面。
    /// </summary>
    /// <param name="linkVM">要查看详情的跨度链接视图模型。</param>
    public async Task OnViewDetailsAsync(SpanLinkViewModel linkVM)
    {
        // 等待跨度数据可用
        var available = await MetricsHelpers.WaitForSpanToBeAvailableAsync(
            traceId: linkVM.TraceId,
            spanId: linkVM.SpanId,
            getSpan: TelemetryRepository.GetSpan,
            DialogService,
            InvokeAsync,
            DialogsLoc,
            _cts.Token).ConfigureAwait(false);

        if (available)
        {
            // 导航到跨度详情页面
            NavigationManager.NavigateTo(DashboardUrls.TraceDetailUrl(linkVM.TraceId, spanId: linkVM.SpanId));
        }
    }

    /// <summary>
    /// 实现IDisposable接口的方法，取消并释放令牌源。
    /// </summary>
    public void Dispose()
    {
        // 取消令牌源
        _cts.Cancel();
        // 释放令牌源资源
        _cts.Dispose();
    }
}
